﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.ServiceLocation;
using Pap.Core.Utilities;
using Pap.Data;
using Pap.Data.Abstraction.Utils;
using Pap.Services;
using Pap.Services.FinancialServices;
using Pap.Services.WorkflowEngine;
using Pap.Web.Utils;

namespace Pap.Web.Modules.Loan.ViewModel
{
    /// <summary>
    /// 
    /// </summary>
    public class LoanExecutionListViewModel : IViewModel
    {
        /// <summary>
        /// 
        /// </summary>
        ILoanRepository _LoanRepository;
        /// <summary>
        /// 
        /// </summary>
        ILoanExecutionService _LoanExecutionService;
        /// <summary>
        /// 
        /// </summary>
        IWorkflowService _WorkflowService;
        /// <summary>
        /// 
        /// </summary>
        IAdministrationRepository _AdministrationRepository;
        /// <summary>
        /// 
        /// </summary>
        IFinanceService _FinanceService;
        /// <summary>
        /// 
        /// </summary>
        ILookUpRepository _LookupRepository;

        /// <summary>
        /// Initializes a new instance of the <see cref="LoanExecutionListViewModel"/> class.
        /// </summary>
        /// <param name="loanRepository">The loan repository.</param>
        /// <param name="workflowService">The workflow service.</param>
        /// <param name="loanService">The loan service.</param>
        /// <param name="administrationRepository">The administration repository.</param>
        /// <param name="financeService">The finance service.</param>
        /// <param name="lookupRepository">The lookup repository.</param>
        public LoanExecutionListViewModel(ILoanRepository loanRepository, IWorkflowService workflowService,
            ILoanExecutionService loanService, IAdministrationRepository administrationRepository,
            IFinanceService financeService, ILookUpRepository lookupRepository)
        {
            _LoanRepository = loanRepository;
            _WorkflowService = workflowService;
            _LoanExecutionService = loanService;
            _AdministrationRepository = administrationRepository;
            _FinanceService = financeService;
            _LookupRepository = lookupRepository;
        }

        /// <summary>
        /// Gets the execution boards.
        /// </summary>
        /// <returns></returns>
        public IEnumerable GetExecutionBoards()
        {
            IEnumerable<IProject> userProjects = _AdministrationRepository.GetUserProjects(RegistryInfo.UserID);
            var boardsIds = _WorkflowService.GetInStep(Steps.BoardExecutionList);
            return (from board in _LoanRepository.FindBoard(b => boardsIds.Contains(b.BoardID))
                    where board.BoardLoans != null && userProjects.Contains(board.BoardLoans.First().LoanObj.ProjectStage.ProjectObj)
                    orderby board.BoardNumber ascending
                    select new
                    {
                        BoardID = board.BoardID,
                        BoardNumber = board.BoardNumber,
                        BoardDate = board.BoardDate,
                        PreviousNotes = _WorkflowService.GetStep(_WorkflowService.GetProcessInstanceIDByEntity(board.BoardID)).PreviousNotes,
                    });
        }

        /// <summary>
        /// Gets the executed boards.
        /// </summary>
        /// <returns></returns>
        public IEnumerable GetExecutedBoards()
        {
            IEnumerable<IProject> userProjects = _AdministrationRepository.GetUserProjects(RegistryInfo.UserID);
            var boardsIds = _WorkflowService.GetInStep(Steps.BoardClosedList);
            return (from board in _LoanRepository.FindBoard(b => boardsIds.Contains(b.BoardID))
                    where board.BoardLoans != null && userProjects.Contains(board.BoardLoans.First().LoanObj.ProjectStage.ProjectObj)
                    orderby board.BoardNumber ascending
                    select new
                    {
                        BoardID = board.BoardID,
                        BoardNumber = board.BoardNumber,
                        BoardDate = board.BoardDate,
                        PreviousNotes = _WorkflowService.GetStep(_WorkflowService.GetProcessInstanceIDByEntity(board.BoardID)).PreviousNotes,
                    });
        }

        /// <summary>
        /// Gets the waiting boards.
        /// </summary>
        /// <returns></returns>
        public IEnumerable GetWaitingBoards()
        {
            IEnumerable<IProject> userProjects = _AdministrationRepository.GetUserProjects(RegistryInfo.UserID);
            var boardsIds = _WorkflowService.GetInStep(Steps.BoradCEOList);
            return (from board in _LoanRepository.FindBoard(b => boardsIds.Contains(b.BoardID))
                    where board.BoardLoans != null && userProjects.Contains(board.BoardLoans.First().LoanObj.ProjectStage.ProjectObj)
                    orderby board.BoardNumber ascending
                    select new
                    {
                        BoardID = board.BoardID,
                        BoardNumber = board.BoardNumber,
                        BoardDate = board.BoardDate,
                        PreviousNotes = _WorkflowService.GetStep(_WorkflowService.GetProcessInstanceIDByEntity(board.BoardID)).PreviousNotes,
                    });
        }

        /// <summary>
        /// Gets the board loans.
        /// </summary>
        /// <param name="boardID">The board ID.</param>
        /// <returns></returns>
        public IEnumerable GetBoardLoans(Guid boardID)
        {
            var loans = _WorkflowService.GetInStep(Steps.ExecutionWaitingList);
            var result = (from boardLoan in _LoanRepository.FindBoardLoans(bl => bl.BoardID == boardID)
                          orderby boardLoan.LoanObj.LoanNumber ascending
                          select new
                          {
                              LoanID = boardLoan.LoanID,
                              LoanNumber = boardLoan.LoanObj.LoanNumber,
                              LoanCreatedDate = boardLoan.LoanObj.LoanCreatedDate,
                              CustomerName = boardLoan.LoanObj.CustomerObj.CustomerName,
                              LoanType = boardLoan.LoanObj.LoanTypeObj.Name,
                              LoanAmount = boardLoan.BoardLoanAmount,
                              PaymentPeriod = boardLoan.BoardPaymentPeriod,
                              PaymentMethod = boardLoan.BoardPaymentMethodObj.LoanPaymentMethodName,
                              PreviousNotes = _WorkflowService.GetStep(_WorkflowService.GetProcessInstanceIDByEntity(boardLoan.LoanID)).PreviousNotes,
                              IsWaiting = (loans.Contains(boardLoan.LoanID))
                          });
            return result;
        }



        /// <summary>
        /// Gets the loan execution.
        /// </summary>
        /// <param name="loanID">The loan ID.</param>
        /// <returns></returns>
        public ILoanExecution GetLoanExecution(Guid loanID)
        {
            var exections = _LoanRepository.GetCustomerLoanByID(loanID).LoanExecutionLst;
            if (exections.Count() > 0)
                return exections.Where(ex => ex.LoanExecutionStatus).OrderBy(ex => ex.ExecutionDate).Last();
            else return null;
        }

        /// <summary>
        /// Gets the execution installments.
        /// </summary>
        /// <param name="loanID">The loan ID.</param>
        /// <param name="executionDate">The execution date.</param>
        /// <param name="checkNumber">The check number.</param>
        /// <param name="checkDate">The check date.</param>
        /// <param name="guarantorName">Name of the guarantor.</param>
        /// <param name="guarantorSSN">The guarantor SSN.</param>
        /// <param name="notes">The notes.</param>
        /// <returns></returns>
        public IEnumerable<ILoanExecutionInstallment> GetExecutionInstallments(Guid loanID, DateTime executionDate, string checkNumber, DateTime checkDate, string guarantorName, string guarantorSSN, string notes)
        {
            ICustomerLoan loan = _LoanRepository.GetCustomerLoanByID(loanID);
            IBoardLoan boardLoan = _LoanRepository.FindBoardLoans(b => b.LoanID == loanID).Last();
            ILoanExecution loanExecution = _LoanExecutionService.CreateLoanExecution(loan, boardLoan, executionDate, checkNumber, checkDate, guarantorName, guarantorSSN, notes, false);
            // _LoanRepository.CreateLoanExecution(loanExecution);
            return _LoanExecutionService.CreateLoanExecutionInstallments(loanExecution);
        }

        /// <summary>
        /// Determines whether [has enough money] [the specified loan ID].
        /// </summary>
        /// <param name="loanID">The loan ID.</param>
        /// <returns>
        ///   <c>true</c> if [has enough money] [the specified loan ID]; otherwise, <c>false</c>.
        /// </returns>
        public bool HasEnoughMoney(Guid loanID)
        {
            ICustomerLoan loan = _LoanRepository.GetCustomerLoanByID(loanID);
            IBoardLoan boardLoan = _LoanRepository.FindBoardLoans(b => b.LoanID == loanID).Last();
            var commiteeBankAccount = _AdministrationRepository.FindCommitteeBankAccounts(
                bankAcc => bankAcc.CommitteeID == RegistryInfo.CommitteeID && bankAcc.ProjectID == loan.ProjectStage.ProjectID).First();
            double accountbalance = _FinanceService.GetBankAccountBalance(commiteeBankAccount);

            return accountbalance >= boardLoan.BoardLoanAmount;
        }

        /// <summary>
        /// Refuses the loan execution.
        /// </summary>
        /// <param name="loanID">The loan ID.</param>
        /// <param name="notes">The notes.</param>
        public void RefuseLoanExecution(Guid loanID, string notes)
        {
            using (IUnitOfWork trancaction = ServiceLocator.Current.GetInstance<IUnitOfWork>())
            {
                ICustomerLoan loan = _LoanRepository.GetCustomerLoanByID(loanID);
                IBoardLoan boardLoan = _LoanRepository.FindBoardLoans(b => b.LoanID == loanID).Last();
                if (loan.LoanExecutionLst.Count() > 0)
                {
                    _LoanRepository.DeleteLoanExecution(loan.LoanExecutionLst.Last());
                }
                Guid processInstanceID = _WorkflowService.GetProcessInstanceIDByEntity(loanID);
                _WorkflowService.Move(ActionTypes.Refuse, processInstanceID, RegistryInfo.UserID, notes, null);

                //check if to close the board
                IBoard board = _LoanRepository.GetBoardByID(boardLoan.BoardID);

                IEnumerable<Guid> waitingLoans = _WorkflowService.GetInStep(Steps.ExecutionWaitingList).Union(_WorkflowService.GetInStep(Steps.ReviewerExecutionOldDateApprove));
                waitingLoans = waitingLoans.Where(laonId => laonId != loan.LoanID);
                if (board.BoardLoans.Where(bl => waitingLoans.Contains(bl.LoanID)).Count() == 0)
                {
                    processInstanceID = _WorkflowService.GetProcessInstanceIDByEntity(board.BoardID);
                    _WorkflowService.Move(ActionTypes.Submit, processInstanceID, RegistryInfo.UserID, DateTime.Now.ToString("dd-MM-yyyy"), null);
                }
                trancaction.Commit();
            }
        }

        /// <summary>
        /// Saves the loan execution.
        /// </summary>
        /// <param name="loanID">The loan ID.</param>
        /// <param name="executionDate">The execution date.</param>
        /// <param name="checkNumber">The check number.</param>
        /// <param name="checkDate">The check date.</param>
        /// <param name="guarantorName">Name of the guarantor.</param>
        /// <param name="guarantorSSN">The guarantor SSN.</param>
        /// <param name="notes">The notes.</param>
        /// <param name="attachmentsID">The attachments ID.</param>
        /// <param name="deletedIDs">The deleted I ds.</param>
        /// <returns></returns>
        public bool SaveLoanExecution(Guid loanID, DateTime executionDate, string checkNumber, DateTime checkDate, string guarantorName, string guarantorSSN, string notes, IEnumerable<Guid> attachmentsID, IEnumerable<Guid> deletedIDs)
        {
            using (IUnitOfWork trancaction = ServiceLocator.Current.GetInstance<IUnitOfWork>())
            {
                ICustomerLoan loan = _LoanRepository.GetCustomerLoanByID(loanID);
                IBoardLoan boardLoan = _LoanRepository.FindBoardLoans(b => b.LoanID == loanID).Last();
                ILoanExecution loanExecution = null;
                if (loan.LoanExecutionLst.Count() == 0)
                {
                    loanExecution = _LoanExecutionService.CreateLoanExecution(loan, boardLoan, executionDate, checkNumber, checkDate, guarantorName, guarantorSSN, notes, false);
                    _LoanRepository.CreateLoanExecution(loanExecution);
                    if (loanExecution == null)
                        return false;
                }
                else
                {
                    loanExecution = loan.LoanExecutionLst.Last();
                    loanExecution.CheckDate = checkDate;
                    loanExecution.CheckNumber = checkNumber;
                    loanExecution.ExecutionDate = executionDate;
                    loanExecution.GuarantorName = guarantorName;
                    loanExecution.GuarantorSSN = guarantorSSN;
                }

                foreach (var item in attachmentsID)
                {
                    if (loanExecution.LoanExecutionAttachmentsLst.Where(att => att.Attachment == item).Count() == 0)
                    {
                        ILoanExecutionAttachment attachment = ServiceLocator.Current.GetInstance<ILoanExecutionAttachment>();
                        attachment.LoanExecutionAttachmentID = Guid.NewGuid();
                        attachment.LoanExecutionID = loanExecution.LoanExecutionID;
                        attachment.Attachment = item;
                        _LoanRepository.CreateLoanExecutionAttachment(attachment);
                    }
                }
                foreach (var imageID in deletedIDs)
                {
                    ILoanExecutionAttachment att = _LoanRepository.FindLoanExecutionAttachments(a => a.Attachment == imageID).FirstOrDefault();
                    if (att != null)
                    {
                        _LoanRepository.DeleteLoanExecutonAttachment(att);
                    }
                }

                trancaction.Commit();
                return true;
            }
        }

        /// <summary>
        /// Accepts the loan execution.
        /// </summary>
        /// <param name="loanID">The loan ID.</param>
        /// <param name="executionDate">The execution date.</param>
        /// <param name="checkNumber">The check number.</param>
        /// <param name="checkDate">The check date.</param>
        /// <param name="guarantorName">Name of the guarantor.</param>
        /// <param name="guarantorSSN">The guarantor SSN.</param>
        /// <param name="notes">The notes.</param>
        /// <param name="attachmentsID">The attachments ID.</param>
        /// <param name="deletedIDs">The deleted I ds.</param>
        /// <returns></returns>
        public bool AcceptLoanExecution(Guid loanID, DateTime executionDate, string checkNumber, DateTime checkDate, string guarantorName, string guarantorSSN, string notes, IEnumerable<Guid> attachmentsID, IEnumerable<Guid> deletedIDs)
        {
            using (IUnitOfWork trancaction = ServiceLocator.Current.GetInstance<IUnitOfWork>())
            {
                ICustomerLoan loan = _LoanRepository.GetCustomerLoanByID(loanID);
                IBoardLoan boardLoan = _LoanRepository.FindBoardLoans(b => b.LoanID == loanID).Last();
                ILoanExecution loanExecution = null;
                if (loan.LoanExecutionLst.Count() == 0)
                {
                    loanExecution = _LoanExecutionService.CreateLoanExecution(loan, boardLoan, executionDate, checkNumber, checkDate, guarantorName, guarantorSSN, notes, true);
                    _LoanRepository.CreateLoanExecution(loanExecution);
                    if (loanExecution == null)
                        return false;
                }
                else
                {
                    loanExecution = loan.LoanExecutionLst.Last();
                    if (!_LoanExecutionService.UpdateLoanExecution(loanExecution, loan, boardLoan, executionDate, checkNumber, checkDate, guarantorName, guarantorSSN, notes))
                        return false;
                }

                var committeeBankAccount =
                    _AdministrationRepository.FindCommitteeBankAccounts(
                        c => c.CommitteeID == RegistryInfo.CommitteeID &&
                             c.ProjectID == loan.ProjectStage.ProjectID).SingleOrDefault();
                if (committeeBankAccount == null)
                    return false;
                committeeBankAccount.Balance = committeeBankAccount.Balance.GetValueOrDefault(0) -
                                               loanExecution.LoanAmount;

                foreach (var item in attachmentsID)
                {
                    if (loanExecution.LoanExecutionAttachmentsLst.Where(att => att.Attachment == item).Count() == 0)
                    {
                        ILoanExecutionAttachment attachment = ServiceLocator.Current.GetInstance<ILoanExecutionAttachment>();
                        attachment.LoanExecutionAttachmentID = Guid.NewGuid();
                        attachment.LoanExecutionID = loanExecution.LoanExecutionID;
                        attachment.Attachment = item;
                        _LoanRepository.CreateLoanExecutionAttachment(attachment);
                    }
                }
                foreach (var imageID in deletedIDs)
                {
                    ILoanExecutionAttachment att = _LoanRepository.FindLoanExecutionAttachments(a => a.Attachment == imageID).FirstOrDefault();
                    if (att != null)
                    {
                        _LoanRepository.DeleteLoanExecutonAttachment(att);
                    }
                }

                Guid processInstanceID = _WorkflowService.GetProcessInstanceIDByEntity(loanID);
                _WorkflowService.Move(ActionTypes.Accept, processInstanceID, RegistryInfo.UserID, notes, null);

                //check if to close the board
                IBoard board = _LoanRepository.GetBoardByID(boardLoan.BoardID);

                IEnumerable<Guid> waitingLoans = _WorkflowService.GetInStep(Steps.ExecutionWaitingList).Union(_WorkflowService.GetInStep(Steps.ReviewerExecutionOldDateApprove));
                waitingLoans = waitingLoans.Where(laonId => laonId != loan.LoanID);
                if (board.BoardLoans.Where(bl => waitingLoans.Contains(bl.LoanID)).Count() == 0)
                {
                    processInstanceID = _WorkflowService.GetProcessInstanceIDByEntity(board.BoardID);
                    _WorkflowService.Move(ActionTypes.Submit, processInstanceID, RegistryInfo.UserID, DateTime.Now.ToString("dd-MM-yyyy"), null);
                }

                trancaction.Commit();
                return true;
            }
        }

        /// <summary>
        /// Requests the old date execution.
        /// </summary>
        /// <param name="loanID">The loan ID.</param>
        /// <param name="executionDate">The execution date.</param>
        /// <param name="notes">The notes.</param>
        public void RequestOldDateExecution(Guid loanID, DateTime executionDate, string notes)
        {
            using (IUnitOfWork trancaction = ServiceLocator.Current.GetInstance<IUnitOfWork>())
            {
                Guid processInstanceID = _WorkflowService.GetProcessInstanceIDByEntity(loanID);
                _WorkflowService.Move(ActionTypes.AskForOldDate, processInstanceID, RegistryInfo.UserID, notes, executionDate);
                trancaction.Commit();
            }
        }

        /// <summary>
        /// Validates the gurantor availability.
        /// </summary>
        /// <param name="gurantSSID">The gurant SSID.</param>
        /// <returns></returns>
        public bool ValidateGurantorAvailability(string gurantSSID)
        {
            return _LoanExecutionService.ValidateGurantor(gurantSSID);
        }

        /// <summary>
        /// Validates the gurantor warning.
        /// </summary>
        /// <param name="gurantSSID">The gurant SSID.</param>
        /// <returns></returns>
        public bool ValidateGurantorWarning(string gurantSSID)
        {
            return _LoanExecutionService.ValidateGurantorWarning(gurantSSID);
        }

        /// <summary>
        /// Determines whether the specified SSN is procecuted.
        /// </summary>
        /// <param name="ssn">The SSN.</param>
        /// <returns>
        ///   <c>true</c> if the specified SSN is procecuted; otherwise, <c>false</c>.
        /// </returns>
        public bool IsProcecuted(string ssn)
        {
            var customer = _LoanRepository.FindCustomers(cust => cust.SSN.Equals(ssn)).FirstOrDefault();

            bool hasTrials = false;
            var priviousTrialsCloseTypesIDs = _LookupRepository.GetPreviousTrialsCloseType().Select(lookup => lookup.ID);
            if (customer != null)
            {
                hasTrials = (from loan in _LoanRepository.FindCustomerLoans(c => true)
                             join loanHistory in _LoanRepository.FindCustomerHistories(cH => true)
                             on loan.LoanID equals loanHistory.LoanID
                             where loan.CustomerID == customer.CustomerID && priviousTrialsCloseTypesIDs.Contains(loanHistory.LoanHistoryCloseTypeID.Value)
                             select loan.LoanAmount).Count() > 0;
            }
            return hasTrials;
        }

        /// <summary>
        /// Gets the old date.
        /// </summary>
        /// <param name="loanID">The loan ID.</param>
        /// <returns></returns>
        public DateTime? GetOldDate(Guid loanID)
        {
            var loanStep = _WorkflowService.GetStep(_WorkflowService.GetProcessInstanceIDByEntity(loanID));
            return loanStep.CompletionDate;
        }
    }
}
